namespace How_toEval;

public class VarScanner
{
    public Dictionary<AbstractSyntaxTree, int> VarsSkip(AbstractSyntaxTree exp)
    {
        Scopes = DefScanner.Scopes(exp);
        ScopeStack.AddFirst(DefScanner.BuiltinScope);
        ScopeStack.AddFirst(DefScanner.GlobalScope);
        ResolveExpression(exp);
        return varskip;
    }

    public VarScanner(DefineScanner scanner)
    {
        varskip = new Dictionary<AbstractSyntaxTree, int>(comparer: ReferenceEqualityComparer.Instance);
        ScopeStack = new LinkedList<HashSet<string>>();
        DefScanner = scanner;
    }

    Dictionary<AbstractSyntaxTree, HashSet<string>>? Scopes;
    DefineScanner DefScanner { get; }
    LinkedList<HashSet<string>> ScopeStack { get; set; }

    private void EnterScope()
    {
        ScopeStack.AddFirst(new HashSet<string>());
    }

    private void ExitScope()
    {
        ScopeStack.RemoveFirst();
    }

    public void ResolveExpression(AbstractSyntaxTree exp)
    {
        switch (exp)
        {
            case AbstractSyntaxTree.Group g: ResolveGroup(g); break;
            case AbstractSyntaxTree.Var v: ResolveVar(v); break;
            case AbstractSyntaxTree.Call c: ResolveCall(c); break;
            case AbstractSyntaxTree.Define d: ResolveDefine(d); break;
            case AbstractSyntaxTree.Function f: ResolveFunction(f); break;
            case AbstractSyntaxTree.Integer i: ResolveInteger(i); break;
            case AbstractSyntaxTree.BinaryOperateNode b: ResolveBinaryNode(b); break;
            case AbstractSyntaxTree.AnnotatedExpression a: ResolvedAnnotated(a); break;
            case AbstractSyntaxTree.Float _: break;
            default: throw new Exception($"SemanticScanner cannot handle such type: {exp.GetType()}");
        }
    }


    private void ResolvedAnnotated(AbstractSyntaxTree.AnnotatedExpression a)
    {
        ResolveExpression(a.Expression);
    }

    private void ResolveBinaryNode(AbstractSyntaxTree.BinaryOperateNode b)
    {
        ResolveExpression(b.Left);
        ResolveExpression(b.Right);
    }

    private void ResolveInteger(AbstractSyntaxTree.Integer i)
    {
        return;
    }

    private void ResolveFunction(AbstractSyntaxTree.Function f)
    {
        WithNewScope(() =>
        {
            ResolveExpression(f.Body);
        }, Scopes![f]);
    }

    private void WithNewScope(Action action, HashSet<string> scope)
    {
        ScopeStack.AddFirst(scope);
        action();
        ExitScope();
    }

    private void ResolveDefine(AbstractSyntaxTree.Define d)
    {
        ResolveExpression(d.Value);
    }

    private void ResolveCall(AbstractSyntaxTree.Call c)
    {
        foreach (var arg in c.Arguments)
        {
            ResolveExpression(arg);
        }

        ResolveExpression(c.Called);
    }

    private void ResolveVar(AbstractSyntaxTree.Var v)
    {
        int i = 0;
        foreach (var scope in ScopeStack)
        {
            if (scope.Contains(v.Name))
            {
                varskip.TryAdd(v, i);
                return;
            }
            i++;
        }
        throw new Exception($"Name {v.Name} cannot be found");
    }

    private void ResolveGroup(AbstractSyntaxTree.Group g)
    {
        foreach (var exp in g.Leaves)
        {
            ResolveExpression(exp);
        }
    }

    Dictionary<AbstractSyntaxTree, int> varskip;
}
